var ui = (function(){
    return {
        version:'0.1.0',

        //判断当前窗口对象是否是框架顶层
        //入参 w[object window] 窗口对象，可选，不提供的话默认取当前窗口
        //返回 [Boolean]
        isTop: function(w) {
            w = w || window;
            return (w.parent == w.self || ! (w.parent != w.self && w.parent.ui && w.parent.ui.name == 'JROS Web Framework'));
        },
        //获取顶层窗口对象
        //返回 [object window] 最顶层的、加载了JROS web框架的页面所在的窗口对象
        getTop:function(){
            var w = window;
            while(!ui.isTop(w)){w = w.parent;}
            return w;
        },
        //从顶层框架获取一个全局唯一的id
        //返回 [String]
        getId:function(){
            var w = ui.getTop();
            w.ui._idCount++;
            return 'id_'+w.ui._idCount;
        },
        //在主功能区中加载子页面
        //入参 url[String]
        loadPage:function(url){
            var w = ui.getTop();
            new w.JROS_LOADER({position:'absolute',className:'jros_page_loader',animType:'3',background:'#333',padding:8}).draw();
            setTimeout(function(){
                w.$('#mainframe').attr('src',url);
            },0);
        },
        //加载页面逻辑
        //入参 clsName[String] 字符串格式的页面逻辑类名
        loadPageClass:function(clsName){
            var cls = eval(clsName);
            cls.prototype = new JROS_PAGE_BASE();
            cls.prototype.constructor = cls;
            var obj = new cls();
            window['o'+clsName] = obj;
            $(function(){
                setTimeout(function(){//异步加载onbeforeload
                    obj.onbeforeload();
                    setTimeout(function(){//异步加载onload
                        obj.onload();
                        setTimeout(function(){//异步加载onafterload
                            obj.onafterload();
                        },0);
                    },0);
                },0);
                $(window).bind('resize',{o:obj},function(ev){
                    ev.data.o.onresize(ev);
                });
                $(window).bind('scroll',{o:obj},function(ev){
                    ev.data.o.onscroll(ev);
                });
                $(window).bind('beforeunload',{o:obj},function(ev){
                    ev.data.o.onbeforeunload(ev);
                });
                $(window).bind('unload',{o:obj},function(ev){
                    ev.data.o.onunload(ev);
                });
            });
        },
        //获取页面URL参数
        //入参  str[String] 字符串格式的URL路径，可选，如不提供默认取当前页面URL
        //返回  [object] url参数键值对，key为参数名，value 为参数值
        getUrlParams:function(str){
            str = decodeURI(str||location.href);
            var idx = str.indexOf('?'),params = {},ary=[];
            if(idx !=-1){
                str = str.substring(idx+1,str.length);
                ary = str.split('&');
                for(var i=0;i<ary.length;i++){
                    var _idx = ary[i].indexOf('=');
                    params[ary[i].substring(0,_idx)] = ary[i].substring(_idx+1,ary[i].length);
                }
            }
            return params;
        },
        //从服务端请求数据
        //入参 ajaxParams[Object] 字符串格式的ajax请求参数，支持所有jQuery API,并额外提供：
        //			data[Array] 必选参数，请求数据，详见框架API文档
        //			localData[Boolean] 可选参数，默认为false，为true时将不再请求服务端接口，而是请求模拟数据(不支持打包批量请求)
        //			loader[Object] 可选参数，请求时绘制的动画图标，设置为false 将不绘制图标

        request:function(ap){
            var _errorFunc = ap.error,_completeFunc = ap.complete,_successFunc = ap.success;
            var _checkFormat = function(o){
                var obj = new Array();
                if(typeof(o)=='string'){
                    obj = JSON.parse(o);
                }else{
                    obj = o;
                }
                for(var i=0;i<obj.length;i++){
                    obj[i].flag = (obj[i].flag == '-130011')?'1':obj[i].flag;
                    obj[i].data.record1 = obj[i].data.record1||[];
                    if(obj[i].meta.length=='0'){
                        obj[i].meta.length = '1';
                        obj[i].meta.record1 = {alllength:'0',length:'0'};
                    }
                };
                return obj;
            };
            ap.url = ap.url||'/index';
            ap.dataType = ap.dataType||'json';
            ap.processData = false;
            ap.contentType= "application/json;charset:UTF-8";
            ap.data = ap.data||{};
            ap.type = ap.type||'POST';

            ap.error = function(jqXHR, textStatus, errorThrown){
                if(_errorFunc)_errorFunc(jqXHR, textStatus, errorThrown);
                throw new Error('AJAX Error: ' + textStatus);
            };
            ap.complete = function(jqXHR, textStatus){
                if(_completeFunc)_completeFunc(jqXHR, textStatus);
            };
            ap.success = function(rtn){
                rtn = _checkFormat(rtn);
                if(_successFunc)_successFunc(rtn);
            };
            //判断是否模拟数据
            if(ap.localData){
                ap.url = '../../JROS/localData/'+ap.data[0].code+'.json';
                ap.data = {};
                ap.type='GET';
            }
            ap.data = JSON.stringify(ap.data);
            $.ajax(ap);
        },
        //转义文本串，将<转义为&lt; > &gt; " &quot; ' &apos;
        text:function(str){
            str = String(str);
            var less = /</g,greater = />/g,quot = /\"/g,apos=/\'/g;
            return str.replace(less, '&lt;').replace(greater, '&gt;').replace(quot,'&quot;').replace(apos,'&aps;');
        }
    };
})();

//常用工具类
if (typeof Kit == "undefined"){
    var Kit = {};
}

Kit.isEmpty = function (value) {
    if (value === null || value === undefined || value === "") {
        return true;
    } else if ((value instanceof Number || typeof(value) === "number") && isNaN(value)) {
        return true;
    } else {
        return false;
    }
};

/**
 * 判断是否成年人
 */
Kit.isAdult = function (sBirthday, sServerDate) {
    var year = sServerDate.substr(0, 4);
    var month = sServerDate.substr(4, 2);
    var day = sServerDate.substr(6, 2);
    var birthyear = sBirthday.substr(0, 4);
    var birthmonth = sBirthday.substr(4, 2);
    var birthday = sBirthday.substr(6, 2);
    var flag = year - birthyear;
    return flag > 18 || ((flag == 18) && (birthmonth < month)) || ((flag == 18) && (birthmonth == month) && (birthday < day));
};

/**
 * 跳转到指定URL
 */
Kit.gotoPage = function (url) {
    window.location.href = encodeURI(url);
};

/**
 * 过滤空字符串
 */
Kit.Trim = function (sValue) {
    if(Kit.isEmpty(sValue)){
        return "";
    }
    return sValue.replace(/\s/g,"");
};

Kit.checkSeries = function (value) {
    for (var i = 0; i < value.length; i++) {
        var charpwd1 = value.charCodeAt(i);
        var charpwd2 = value.charCodeAt(i + 1);
        var charpwd3 = value.charCodeAt(i + 2);
        if (isNaN(charpwd2) || isNaN(charpwd3) || isNaN(charpwd1)) {
            return true;
        }

        if ((charpwd2 == (charpwd1 + 1) && charpwd3 == (charpwd2 + 1))
            || (charpwd2 == (charpwd1 - 1) && charpwd3 == (charpwd2 - 1))) {
            return false;
        }
    }
    return true;
};

/**
 * 格式化整型日期,仅检查位数和字符有效性，如果无效返回
 * @param date 可以是Date、int、string类型
 * @param sSplit 分隔符,默认'-','CN'表示用年月日分隔
 * @returns string
 */
Kit.fmtDate = function (date, sSplit) {
    var sDate, sY, sM, sD;
    if (date instanceof Date) {
        sY = date.getFullYear();
        sM = date.getMonth() + 1 + "";
        if (sM.length === 1) {
            sM = "0" + sM;
        }

        sD = date.getDate() + "";
        if (sD.length === 1)
            sD = "0" + sD;
    } else {
        sDate = date.toString();
        if (/^\d{8}$/.test(sDate)) {
            sY = sDate.substring(0, 4);
            sM = sDate.substring(4, 6);
            sD = sDate.substring(6, 8);
        } else
            return "-";
    }

    if (sSplit === "CN") {
        sDate = sY + "年" + sM + "月" + sD + "日";
    } else {
        if (sSplit === "" || sSplit == null) {
            sSplit = "-";
        }
        sDate = sY + sSplit + sM + sSplit + sD;
    }
    return sDate;
};

/**
 * 格式化日期为yyyyMMdd格式
 */
Kit.reDate = function (str){
    if (typeof str == "string" && str.length == 10) {
        return str.substring(0, 4) + str.substring(5, 7) + str.substring(8, 10);
    }
    return str;
};

/*
*   日期格式化为指定格式，
 *   fmt可以是 yyyy-MM-dd hh:mm:ss
*/
Kit.simpleFmtDate = function (date,fmt) {
    if (date === null || date === undefined || date === ""){
        return "";
    }

    if((typeof  date) == "number") {
        date = new Date(date);
    }
    var o = {
        "M+": date.getMonth() + 1, //月份
        "d+": date.getDate(), //日
        "h+": date.getHours(), //小时
        "m+": date.getMinutes(), //分
        "s+": date.getSeconds(), //秒
        "q+": Math.floor((date.getMonth() + 3) / 3), //季度
        "S": date.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}


/**
 * 将人民币金额转换为大写
 * @param fAmount
 * @returns {sring}
 */
Kit.capitalRMB = function (fAmount, sSuffix) {
    //字符长度
    function LengthB(str) {
        var p1 = new RegExp('%u..', 'g');
        var p2 = new RegExp('%.', 'g');
        return escape(str).replace(p1, '').replace(p2, '').length;
    }

    var low;
    var i, k, j, l_xx1;
    var cap = "", xx1, unit;
    var a = ("" + fAmount).replace(/(^0*)/g, "").split(".");

    var digits = "零壹贰叁肆伍陆柒捌玖";
    var units = "份 点拾佰仟万拾佰仟亿拾佰仟万拾佰仟";
    var digit;
    low = parseFloat(fAmount);
    if (isNaN(low)) {
        return "";
    }

    xx1 = Math.round(low * 100.0) + "";
    l_xx1 = xx1.length;

    for (i = 0; i < l_xx1; i++) {
        j = l_xx1 - 1 - i;
        unit = units.substr(j, 1);
        // 生成大写单位，即'..份拾...'
        k = parseInt(xx1.substr(i, 1));
        digit = digits.substr(k, 1);
        // 生成大写数字, 即'零壹贰叁...'
        cap = cap + digit + unit;
    }

    cap = cap.replace(" ", "");
    cap = cap.replace(/零拾|零佰|零仟/g, "零");
    cap = cap.replace(/零+/g, "零");
    cap = cap.replace(/零亿/g, "亿");
    cap = cap.replace(/零万/g, "万");
    cap = cap.replace(/零点/g, "点");
    cap = cap.replace(/零份/g, "份");
    cap = cap.replace(/亿万/g, "亿");
    cap = cap.replace(/^壹拾/, "拾");
    cap = cap.replace(/点份$/, "份整");
    cap = cap.replace(/份整$/, "");
    cap = cap.replace(/零$/, "");
    cap = cap.replace(/份$/, "");
    if (a.length > 1 && LengthB(a[1]) > 2) {
        cap = '小数点位数不应超过2位';
        return cap;
    }
    if (a.length > 1 && LengthB(a[0]) == 0 && a[1].charAt(0) != '0') {
        cap = cap.replace("", "零点");
    }
    if (a.length > 1 && LengthB(a[0]) == 0 && a[1].charAt(0) == '0') {
        cap = cap.replace("", "零点零");
    }
    if (cap != "" || cap.length > 0) {
        cap = cap + sSuffix;
    }
    return cap;
};
/**
 * 格式化数字
 * @param fData 待格式的数字
 * @param sFormat 格式化掩码，支持：#，0.等字符，可选，则默认KGC.GO_AmountFmt：
 *          ‘#’一位数字，如果是数字末尾且是0则不显示，‘，’分位标志，‘0’一位数字，‘.’小数点，例1234567.1204格式化如下：
 *          #,###（#,###.#0）     1,234,567      #,###.0#            1,234,567.12
 *          #,###.##            1,234,567.12   #,###.0##        1,234,567.12
 *          #,###.00            1,234,567.12
 */
Kit.fmtNumber = function (fData, sFormat) {
    var arrTemp;
    var sInt, sFloat, sFmt,sSign;
    var fmtInt = "", fmtFloat = "";
    sSign = fData >= 0 ? "" : "-";
    fData = Math.abs(fData);
    var sData = fData.toString();
    if (arguments.length == 1) {
        sFormat = "#,##0.00"; // 系统默认金额格式化掩码
    }
    arrTemp = sFormat.match(/[\#\,0\.]+/);
    if (arrTemp != null) {
        sFmt = arrTemp[0];
    } else {
        sFmt = "#,##0.00";
    }
    var reDecimal = /\./;
    if (reDecimal.test(sFmt)) {
        arrTemp = sFmt.split(".");
        fmtInt = arrTemp[0];
        fmtFloat = arrTemp[1];
    } else {
        fmtInt = sFmt;
    }

    if (reDecimal.test(sData)) {
        if (fmtFloat != "") {
            arrTemp = sData.split(".");
            var iPow10 = Math.pow(10, fmtFloat.length);
            var fTemp = Math.round(parseFloat("0." + arrTemp[1]) * iPow10) / iPow10;
            sInt = (Math.floor(fData) + Math.floor(fTemp)).toString();
            if (fTemp == 0 || fTemp == 1)
                sFloat = "0";
            else
                sFloat = fTemp.toString().split(".")[1];
        } else {
            sInt = Math.round(fData).toString();
            sFloat = "";
        }
    } else {
        sInt = sData;
        sFloat = "";
    }

    if (fmtInt != "") {
        var iCommaLen = null;
        var iZeroCount = fmtInt.match(/0*$/)[0].length;
        if (/,/g.test(fmtInt)) {
            iCommaLen = fmtInt.match(/,[^,]*/)[0].length - 1;
        }
        var reg = new RegExp("(\\d{" + iCommaLen + "})", "g");
        if (sInt.length < iZeroCount) {
            sInt = new Array(iZeroCount + 1).join("0") + sInt;
            sInt = sInt.substr(sInt.length - iZeroCount, iZeroCount);
        }
        if (iCommaLen > 0) {
            var idx = sInt.length % iCommaLen;
            sInt = sInt.substr(0, idx) + sInt.substring(idx).replace(reg, ",$1");
        }
        sInt = sInt.replace(/^,/, "");
    }

    if (fmtFloat != "") {
        var iZeroCount = fmtFloat.match(/^0*/)[0].length;
        if (sFloat.length < iZeroCount) {
            sFloat = sFloat + new Array(iZeroCount + 1).join("0");
            if (fmtFloat.length > iZeroCount) {
                var sTemp1 = sFloat.substring(0, iZeroCount);
                var sTemp2 = sFloat.substring(iZeroCount, fmtFloat.length);
                sFloat = sTemp1 + sTemp2.replace(/0*$/, "");
            } else {
                sFloat = sFloat.substring(0, iZeroCount);
            }
        } else {
            sFloat = sFloat.substring(0, fmtFloat.length);
        }
    } else {
        sFloat = "";
    }

    if (sFloat == "") {
        sData = sInt;
    } else {
        sData = sInt + "." + sFloat;
    }
    if (sFormat != sFmt) {
        sData = sFormat.replace(sFmt, sData);
    }
    return sSign + sData;
};

/**
 * 申请金额、份额合并
 */
Kit.doGridAppAmountOrVol = function (o, val) {
    if (o.applicationamount != "0.00") {
        return Kit.fmtNumber(o.applicationamount, "#,###.00") + "（元）";
    } else if (o.applicationvol != "0.00") {
        return Kit.fmtNumber(o.applicationvol, "#,###.00") + "（份）";
    } else {
        return val;
    }
};

/*申请金额、份额合并*/
Kit.doGridAmountOrVol = function (o, val) {
    if (o.aData.applicationamount != "0.00") {
        return Kit.fmtNumber(o.aData.applicationamount, "#,###.00") + "（元）";
    } else if (o.aData.applicationvol != "0.00") {
        return Kit.fmtNumber(o.aData.applicationvol, "#,###.00") + "（份）";
    } else {
        return "-&nbsp;&nbsp;";
    }
};
/*确认金额、份额合并*/
Kit.doGridConAmountOrVol = function (o, val) {
    if (o.aData.confirmedamount != "0.00") {
        return Kit.fmtNumber(o.aData.confirmedamount, "#,###.00") + "（元）";
    } else if (o.aData.confirmedvol != "0.00") {
        return Kit.fmtNumber(o.aData.confirmedvol, "#,###.00") + "（份）";
    } else {
        return "-&nbsp;&nbsp;";
    }
};

/**
 * 从身份证号码中取出生日
 */
Kit.getBirthdayFormIdNo = function (sIdNo) {
    //15位号码
    if (sIdNo.length == 15)
        return "19" + sIdNo.substr(6, 6);
    //18位号码
    return sIdNo.substr(6, 8);
};

/**
 * 从身份证号码中取出性别
 */
Kit.getSexFormIdNo = function (sIdNo) {
    var key=sIdNo.substring(sIdNo.length-2,sIdNo.length-1);
    return (key%2)==0?0:1;
};

/**
 * 验证邮箱格式
 * @param str
 * @returns {boolean}
 */
Kit.validEmail = function(str) {
    var regxp = /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/;
    return regxp.test(str);
}

/**
 * 验证手机号格式
 * @param str
 * @returns {boolean}
 */
Kit.validMobile = function(str) {
    var regxp = /^1[34578]\d{9}$/;
    return regxp.test(str);
}

/**
 * 判断是否为微信浏览器
 */
Kit.checkIsWeiXin = function (){
    var ua = window.navigator.userAgent.toLowerCase();
    if(ua.match(/MicroMessenger/i) == 'micromessenger'){
        return true;
    }else{
        return false;
    }
};

/**
 * 简单检查输入的身份证号是否无效，有效返回false，无效返回true
 */
function isValidIDCardNo(code){
    var Errors = new Array("true",
        "身份证号码位数不对,必须是15位或者18位!",
        "身份证号码出生年月日格式不对!",
        "身份证号码校验位错误!",
        "身份证地区非法!",
        "15位身份证号码由数字组成!",
        "18位身份证号码前17位由数字组成,第18位可以是数字或者大写\"X\"!");
    if (code.length != 15 && code.length != 18) {// 身份证长度不正确
        return Errors[1];
    }
    var area = {
        11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江",
        31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北",43:"湖南",
        44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃",
        63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"
    };
    var Y, JYM;
    var S, M;
    var ereg;
    var idcard_array = new Array();
    idcard_array = code.split("");
    // 地区检验
    if (area[parseInt(code.substr(0, 2))] == null)
        return Errors[4];
    // 身份号码位数及格式检验
    switch (code.length) {
        case 15:
            if (!/^[0-9]{15}$/.test(code)) {
                return Errors[5];
            }
            var sBirthday = "19" + code.substr(6, 2) + "-"
                + Number(code.substr(8, 2)) + "-"
                + Number(code.substr(10, 2));
            var d = new Date(sBirthday.replace(/-/g, "/"));
            var flag = (sBirthday != (d.getFullYear() + "-"
                + (d.getMonth() + 1) + "-" + d.getDate()));
            if (!flag)
                return Errors[0];
            else
                return Errors[2];
            break;
        case 18:
            if (!/^[0-9]{17}([0-9X])$/.test(code)) {
                return Errors[6];
            }

            var sBirthday = code.substr(6, 4) + "-"
                + Number(code.substr(10, 2)) + "-"
                + Number(code.substr(12, 2));
            var d = new Date(sBirthday.replace(/-/g, "/"));
            var flag = (sBirthday != (d.getFullYear() + "-"
                + (d.getMonth() + 1) + "-" + d.getDate()));
            if (!flag) {// 测试出生日期的合法性
                // 计算校验位
                S = (parseInt(idcard_array[0]) + parseInt(idcard_array[10]))
                    * 7
                    + (parseInt(idcard_array[1]) + parseInt(idcard_array[11]))
                    * 9
                    + (parseInt(idcard_array[2]) + parseInt(idcard_array[12]))
                    * 10
                    + (parseInt(idcard_array[3]) + parseInt(idcard_array[13]))
                    * 5
                    + (parseInt(idcard_array[4]) + parseInt(idcard_array[14]))
                    * 8
                    + (parseInt(idcard_array[5]) + parseInt(idcard_array[15]))
                    * 4
                    + (parseInt(idcard_array[6]) + parseInt(idcard_array[16]))
                    * 2 + parseInt(idcard_array[7]) * 1
                    + parseInt(idcard_array[8]) * 6
                    + parseInt(idcard_array[9]) * 3;
                Y = S % 11;
                M = "F";
                JYM = "10X98765432";
                M = JYM.substr(Y, 1);
                // 判断校验位
                if (M == idcard_array[17])
                    return Errors[0];// 检测ID的校验位
                else
                    return Errors[3];
            } else
                return Errors[2];
            break;
        default:
            return Errors[1];
            break;
    }
};

/**
 * 获取用户是否登录
 */
Kit.getCustFlag = function (){
    var custFlag;
    $.ajax({
        url:"/getCustFlag.do",
        async : false,
        success:function(obj){
            if(obj.retCode=="0"){
                custFlag = obj.retData[0][0].belongto;
            }
        }
    });
    return custFlag;
}

/**
 * 保存页面路径日志
 * PID:页面标示(当前文件夹位置，和第几个页面组成的字符串    eg：register1.html 这个页面的pid：0102)
 * PNAME:页面title名称
 */
Kit.saveUrlLog = function (PID,PNAME){
    ui.request({
        url : "../../common/ajax.do",
        data:{code: 'saveUrlLog',params:{"PID":PID,"PNAME":PNAME}},
        type : "POST",
        success: function(obj) {
            console.log("保存访问日志："+obj[0].prompt);
        }
    });
}


/**
 * 获取系统交易日期
 */
Kit.getBizDate = function (){
    if(sessionStorage.getItem("busidate")==undefined||sessionStorage.getItem("busidate")==''){
        ui.request({
            url : "../../common/ajax.do",
            data:{code: 'qryBizDate',params:{}},
            type : "POST",
            async : true,
            success: function(obj) {
                var result = obj[0].data.record1;
                if(result){
                    sessionStorage.setItem("busidate",result[0].busidate);
                    return result[0].busidate;
                }
            }
        });
    }else{
        var busidate = sessionStorage.getItem("busidate");
        return busidate;
    }
};

function ListMap() {
    var sKeyName = "key";        // 索引属性名称
    var sValueName = "value";    // 默认值属性名称
    var oParent = {};            // 数据项MapItem.prototype的值（数据项对象的父类）
    var aoMap = [];              // 存放数据项对象的数组

    // 根据入参构建数据项对象的父类（合并所有数据对象的属性到父类中）
    var iBegin = null;
    var iArgLen = arguments.length;
    if (typeof(arguments[0]) == "string") {
        sKeyName = arguments[0];
        if (typeof(arguments[1]) == "string") {
            if (iArgLen > 0) {
                sValueName = arguments[1];
                iBegin = 2;
            } else
                iBegin = 1;
        }
        for (var i = iBegin; i < iArgLen; i++) {
            if (typeof(arguments[i]) == "object")
                $.extend(oParent, arguments[i]);
        }
    }
    var o = null;
    eval("o={'" + sKeyName + "':'','" + sValueName + "':''}");
    o.getKey = function () {
        return this[sKeyName];
    };
    o.getValue = function () {
        return this[sValueName];
    };
    $.extend(oParent, o);

    /**
     * 内部数据项类，该类继承于oParent
     */
    function MapItem() {
    }

    MapItem.prototype = oParent;

    /**
     * 根据索引属性的属性值查找数据项
     * @param vKey 数据项索引属性值
     * @returns 返回其索引位置，未找到返回-1
     */
    this.find = function (vKey) {
        for (var i = 0; i < aoMap.length; i++) {
            if (aoMap[i][sKeyName] == vKey)
                return i;
        }
        return -1;
    };

    /**
     * 通过数据项在数组中的索引获取映射项，如果索引越界，返回undefined
     * @param {number} iIndex 数据项索引
     * @returns {object} 数据项对象
     */
    this.items = function (iIndex) {
        if (Kit.isEmpty(iIndex)) return undefined;
        if (iIndex < aoMap.length && iIndex >= 0)
            return aoMap[iIndex];
        return undefined;
    };

    /**
     * 通过索引属性值获取该数据项的默认属性值，未找到返回undefined或defaultValue
     * @param vKey 数据项索引属性值
     * @param vDefaultValue 未找到默认返回值
     * @returns 默认属性值
     */
    this.get = function (vKey, vDefaultValue) {
        if ((!vKey) && (vKey != "")) return vDefaultValue;

        var i = this.find(vKey);
        return i >= 0 ? aoMap[i][sValueName] : vDefaultValue;
    };

    /**
     * 通过索引属性值获取该数据项的指定属性的属性，未找到返回undefined或defaultValue
     * @param vKey 数据项索引属性值
     * @param {string} sPropName 返回数据项该属性的属性值
     * @param vDefaultValue 未找到默认返回值
     * @returns 指定属性值
     */
    this.getValue = function (vKey, sPropName, vDefaultValue) {
        if ((!vKey) && (vKey != "")) return vDefaultValue;

        var i = this.find(vKey);
        if (i >= 0) {
            var item = aoMap[i];
            return item.hasOwnProperty(sPropName) ? item[sPropName] : vDefaultValue;
        } else
            return vDefaultValue;
    };
    /**
     * 通过索引属性值获取该数据项并返回，未找到返回undefined或defaultValue
     * @param vKey 数据项索引属性的属性值
     * @param vDefaultValue 未找到默认返回值
     * @returns 数据项对象
     */
    this.getItem = function (vKey, vDefaultValue) {
        if ((!vKey) && (vKey != "")) return vDefaultValue;

        var i = this.find(vKey);
        return i >= 0 ? aoMap[i] : vDefaultValue;
    };

    /**
     * 设置指定数据项默认属性的属性值，如果该数据项不存在则添加
     * @param vKey 数据项索引属性值
     * @param value 默认属性的属性值
     */
    this.set = function (vKey, value) {
        if ((!vKey) && (vKey != "")) return;

        var i = this.find(vKey);
        if (i >= 0)
            aoMap[i][sValueName] = value;
        else {
            var item = new MapItem();
            item[sKeyName] = vKey;
            item[sValueName] = value;
            aoMap.push(item);
        }
    };

    /**
     * 返回指定索引位置的数据项的索引属性值，如果索引越界，返回vDefaultValue
     * @param {number} iIndex 数据对象索引
     * @param vDefaultValue 未找到默认返回值
     * @returns 索引属性值
     */
    this.getKey = function (iIndex, vDefaultValue) {
        var o = this.items(iIndex);
        if (o) {
            return o[sKeyName];
        } else
            return vDefaultValue;
    };

    /**
     * 设置指定数据项指定属性的属性值，如果该数据项不存在则添加
     * @param vKey 数据项索引属性值
     * @param {string} sPropName 返回数据项该属性的属性值
     * @param value 属性值
     */
    this.setValue = function (vKey, sPropName, value) {
        if ((!vKey) && (vKey != "")) return;

        var i = this.find(vKey);
        if (i >= 0)
            aoMap[i][sPropName] = value;
        else {
            var item = new MapItem();
            item[sKeyName] = vKey;
            //if (oParent.hasOwnProperty(sPropName))
            item[sPropName] = value;
            aoMap.push(item);
        }
    };

    /**
     * 设置更新数据项，不存在则添加
     * @param {MapItem} o 数据项对象
     */
    this.setItem = function (o) {
        if (Kit.isEmpty(o)) return;

        // 检查该对象是否包含索引属性名称，不存在则退出
        var vKey = undefined;
        if (o.hasOwnProperty(sKeyName))
            vKey = o[sKeyName];
        if ((!vKey) && (vKey != "")) return;

        var item;
        var i = this.find(vKey);
        if (i >= 0) {
            item = aoMap[i];
        } else {
            item = new MapItem();
            aoMap.push(item);
        }
        // 赋值
        var prop = null;
        for (prop in o) {
            if (oParent.hasOwnProperty(prop)) {
                item[prop] = o[prop];
            }
        }
    };

    /**
     * 移除指定的数据项
     * @param vKey 数据项索引属性值
     */
    this.del = function (vKey) {
        if ((!vKey) && (vKey != "")) return;
        var i = this.find(vKey);
        if (i >= 0)
            aoMap.splice(i, 1);
    };

    /**
     * 返回列表中数据项的数目
     */
    this.count = function () {
        return aoMap.length;
    };

    /**
     * 将oList中的数据项添加到当前列表中，oData为ListMap类型或对象类型
     * @param {ListMap|object} oData 数据项列表
     */
    this.assigned = function (oData) {
        if (oData instanceof ListMap) {
            var iCount = oData.count();
            for (var i = 0; i < iCount; i++) {
                this.setItem(oData.items(i));
            }
        } else if (typeof(oData) === "object") {
            for (var sProp in oData) {
                this.set(sProp, oData[sProp]);
            }
        } else
            throw new Error("oData type error.");
    };

    /**
     * 解析字符串，插入到列表中，字符串格式：key1|value1;key2|value2;...
     * @param {string} sValue
     * @param {string} sSplitChar 每对key/valu之间的分隔符，可选，默认为分号(;)
     */
    this.parse = function (sValue, sSplitChar) {
        if (arguments.length == 1) {
            sSplitChar = ";";
        }
        var aItem;
        var aValue = sValue.split(sSplitChar);
        for (var i = 0; i < aValue.length; i++) {
            aItem = aValue[i].split("|");
            this.set(aItem[0], aItem[1]);
        }
    };

    /**
     * 检查数据项列表是否为空
     * @returns {boolean} false-非空，true-空
     */
    this.isEmpty = function () {
        return aoMap.length <= 0;
    };

    /**
     * 清除映射列表
     */
    this.clear = function () {
        aoMap.length = 0;
    };

    /**
     * 将列表项中的数据转换为JSON对象，转换原则：
     * 以索引属性的属性值为对象属性名，默认属性的属性值为对象属性值，
     * 将列表中的数据项对象的索引属性和默认属性转换为JSON对象
     */
    this.toJSON = function () {
        var oItem;
        var oJSON = {};
        for (var i = 0; i < this.count(); i++) {
            oItem = this.items(i);
            oJSON[oItem[sKeyName]] = oItem[sValueName];
        }
        return oJSON;
    };

    /**
     * 将列表项中的数据转换为JSON对象
     * Key加上"param."前缀
     */
    this.toParamJSON = function () {
        var oItem;
        var oJSON = {};
        for (var i = 0; i < this.count(); i++) {
            oItem = this.items(i);
            oJSON["param." + oItem[sKeyName]] = oItem[sValueName];
        }
        return oJSON;
    };

    /**
     * 以索引属性的属性值为参数名，默认属性的属性值为参数值，将列表中的数据项对象转换为URL查询字符串，
     * 返回查询串中首尾不带？和&；如果列表为空则返回空字符串""
     * @returns {string} URL查询串
     */
    this.toUrlParamStr = function () {
        var oItem;
        var sParams = "";
        var iCount = this.count();
        var sKeyName = this.getKeyName();
        var sValueName = this.getValueName();
        // 取第一个参数
        if (iCount > 0) {
            oItem = this.items(0);
            sParams = oItem[sKeyName] + "=" + oItem[sValueName];
        } else {
            return "".toString();
        }
        // 取剩余参数
        for (var i = 1; i < iCount; i++) {
            oItem = this.items(i);
            sParams += "&" + oItem[sKeyName] + "=" + oItem[sValueName];
        }
        return sParams.toString();
    };

    /**
     * 检查ListMap中的数据项对象是否有指定的属性名称
     * @param {string} sPropertyName 属性名称
     * @returns {boolean} false-不包含，true-包含
     */
    this.itemHasProperty = function (sPropertyName) {
        return (oParent.hasOwnProperty(sPropertyName));
    };

    /**
     * 返回数据项的索引属性名称
     * @returns 属性名称
     */
    this.getKeyName = function () {
        return sKeyName;
    };

    /**
     * 返回数据项的默认值的属性名称
     * @returns 属性名称
     */
    this.getValueName = function () {
        return sValueName;
    };

    /**
     * 返回数据项key数组
     * @returns 属性名称
     */
    this.getKeyNames = function () {
        var keys = new Array();
        var oItem;
        for (var i = 0; i < this.count(); i++) {
            oItem = this.items(i);
            keys.push(oItem[sKeyName]);
        }
        return keys;
    };

    /**
     * 返回数据项value数组
     * @returns 属性名称
     */
    this.getKeyValues = function () {
        var values = new Array();
        var oItem;
        for (var i = 0; i < this.count(); i++) {
            oItem = this.items(i);
            values.push(oItem[sValueName]);
        }
        return values;
    };
}